package fr.ece.ing4.montecarlo.meriaux.lange;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

public class MonteCarlo {

	public double ComputeValue(int z, double S, double X, double t,
			double r, double b, double v, int nSteps) {
		double st = S;
		double dt = t / nSteps;
		Random rand = new Random();
		double drift = (b - (v * v) / 2) * dt;
		double vSqrdt = v * Math.sqrt(dt);
		for (int j = 0; j < nSteps; j++) {
			st = st * Math.exp(drift + vSqrdt * rand.nextGaussian());
		}

		return Math.max(z * (st - X), 0);

	}

	public double ConcurrentMonteCarloStandardOption(String CallPutFlag,
			final double S, final double X, final double t, final double r,
			final double b, final double v, final int nSteps, int nSimulations,
			double blockingCoefficient) throws InterruptedException,
			ExecutionException {
		int y = 0;

		if (CallPutFlag.equals("c")) {
			y = 1;
		} else if (CallPutFlag.equals("p")) {
			y = -1;
		}
		final int z = y;
		final int numberOfCores = Runtime.getRuntime().availableProcessors();
		final int poolSize = (int) (numberOfCores / (1 - blockingCoefficient));

		System.out.println("Number of Cores available is " + numberOfCores);
		System.out.println("Pool size is " + poolSize);
		final List<Callable<Double>> partitions = new ArrayList<Callable<Double>>();
		for (int i = 0; i < nSimulations; i++) {
			partitions.add(new Callable<Double>() {
				public Double call() throws Exception {
					return ComputeValue(z, S, X, t, r, b, v, nSteps);
				}
			});
		}

		final ExecutorService executorPool = Executors
				.newFixedThreadPool(poolSize);

		final List<Future<Double>> ComputedValues = executorPool.invokeAll(
				partitions, 10000, TimeUnit.SECONDS);
		double Sum = 0.0;
		for (final Future<Double> ComputedValue : ComputedValues)
			Sum += ComputedValue.get();

		double result = java.lang.Math.exp((-r * t) * (Sum / nSimulations));
		executorPool.shutdown();		
		return result;
	}

	public double SequentialMonteCarloStandardOption(String CallPutFlag,
			double S, double X, double t, double r, double b, double v,
			int nSteps, int nSimulations) {
		int z = 0;
		double st;
		double dt = t / nSteps;
		double drift = (b - (v * v) / 2) * dt;
		double vSqrdt = v * Math.sqrt(dt);
		Random rand = new Random();
		double sum = 0, result = 0;
		if (CallPutFlag.equals("c")) {
			z = 1;
		} else if (CallPutFlag.equals("p")) {
			z = -1;
		}

		for (int i = 1; i <= nSimulations; i++) {
			st = S;
			for (int j = 1; j <= nSteps; j++) {
				st = st * Math.exp(drift + vSqrdt * rand.nextGaussian());
			}
			sum = sum + Math.max(z * (st - X), 0);
		}
		return result = Math.exp((-r * t) * (sum / nSimulations));

	}

}